#ifndef MOCHA_DETAIL_SCHWARZ_COUNTER_HPP_INCLUDED
#define MOCHA_DETAIL_SCHWARZ_COUNTER_HPP_INCLUDED

/**
 * @file 
 */

#include <functional>

namespace mocha {
/**
 * 
 * @see schwarz_counter
 */
template<typename T>
class basic_schwarz_allocator {
public:
	typedef T object_type;
	static T* alloc() {
		return new T();
	}
	static void free(T* obj) {
		delete obj;
	}
};
/**
 * 
 * @see schwarz_counter
 */
template<typename T, size_t N>
class array_schwarz_allocator {
public:
	typedef T object_type;
	static T* alloc() {
		return new T[N];
	}
	static void free(T* obj) {
		delete [] obj;
	}
}
/**
 * 
 * @see schwarz_counter
 */
template<typename T>
class basic_schwarz_initializer : public std::unary_function<T*, void> {
public:
	void operator()(T* obj) {}
};
// Basic usage: schwarz_counter<foo> foo_schwarz_counter;
/**
 * So-called Schwarz counter.
 * Used to ensure the initialization of static/global objects.
 * A counter can be instantiated within a header to manage a static/global type.
 * @param T Type of the static/global object to be managed.
 * @param A Schwarz allocator type implementing the Schwarz Allocator Model. Used to construct and destruct the static/global object. Note that for each T there should be only one A, otherwise more than one counter will exist for the type T.
 */
template<typename T, typename A = basic_schwarz_allocator<T>, typename I = basic_schwarz_initializer<T> >
class schwarz_counter {
public:
	schwarz_counter() {
		if (0 == count_++) {
			obj_ = A::alloc();
			I init;
			init(obj_);
		}
	}
	~schwarz_counter() {
		if (0 == --count_) {
			A::free(obj_);
		}
	}
private:
	static unsigned int count_;
	static T* obj_;
};
template<typename T> unsigned int schwarz_counter<T>::count_ = 0;
template<typename T> T* schwarz_counter<T>::obj_ = NULL;
}

#endif

